﻿using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;

using NUnit.Framework;

using Uhi.Libraries.Test;

using Uhi.Applications.FileAid.View;

namespace FileAidTest.View
{
    [TestFixture]
    public class TextEditorTest
    {
        #region _cctor

        #region FontFamily

        #region 正常ケース
        [Test, STAThread]
        public void FontFamily_正常ケース()
        {
            var font = new FontFamily();
            var editor = new TextEditor();
            editor.FontFamily = font;
            editor.FontFamily.Is(font);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void FontFamily_Callbackのテスト()
        {
            var font = new FontFamily();
            var editor = new TextEditor();

            editor.AsDynamic().FormattedText = null;

            editor.FontFamily = font;
            (editor.AsDynamic().FormattedText as FormattedText[]).IsNotNull();
        }
        #endregion

        #endregion

        #region FontStyle

        #region 正常ケース
        [Test, STAThread]
        public void FontStyle_正常ケース()
        {
            var style = FontStyles.Italic;   
            var editor = new TextEditor();
            editor.FontStyle = style;
            editor.FontStyle.Is(style);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void FontStyle_Callbackのテスト()
        {
            var style = FontStyles.Italic;
            var editor = new TextEditor();

            editor.AsDynamic().FormattedText = null;

            editor.FontStyle = style;
            (editor.AsDynamic().FormattedText as FormattedText[]).IsNotNull();
        }
        #endregion

        #endregion

        #region FontWeight

        #region 正常ケース
        [Test, STAThread]
        public void FontWeight_正常ケース()
        {
            var weight = FontWeights.Bold;
            var editor = new TextEditor();
            editor.FontWeight = weight;
            editor.FontWeight.Is(weight);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void FontWeight_Callbackのテスト()
        {
            var weight = FontWeights.Bold;
            var editor = new TextEditor();

            editor.AsDynamic().FormattedText = null;

            editor.FontWeight = weight;
            (editor.AsDynamic().FormattedText as FormattedText[]).IsNotNull();
        }
        #endregion

        #endregion

        #region FontStretch

        #region 正常ケース
        [Test, STAThread]
        public void FontStretch_正常ケース()
        {
            var stretch = FontStretches.Expanded;
            var editor = new TextEditor();
            editor.FontStretch = stretch;
            editor.FontStretch.Is(stretch);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void FontStretch_Callbackのテスト()
        {
            var stretches = FontStretches.Expanded;
            var editor = new TextEditor();

            editor.AsDynamic().FormattedText = null;

            editor.FontStretch = stretches;
            (editor.AsDynamic().FormattedText as FormattedText[]).IsNotNull();
        }
        #endregion

        #endregion

        #region FontSize

        #region 正常ケース
        [Test, STAThread]
        public void FontSize_正常ケース()
        {
            var size = 16;
            var editor = new TextEditor();
            editor.FontSize = size;
            editor.FontSize.Is(size);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void FontSize_Callbackのテスト()
        {
            var size = 16;
            var editor = new TextEditor();

            editor.AsDynamic().FormattedText = null;

            editor.FontSize = size;
            (editor.AsDynamic().FormattedText as FormattedText[]).IsNotNull();
        }
        #endregion

        #endregion

        #region Foreground

        #region 正常ケース
        [Test, STAThread]
        public void Foreground_正常ケース()
        {
            var brush = Brushes.AliceBlue;
            var editor = new TextEditor();
            editor.Foreground = brush;
            editor.Foreground.Is(brush);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void Foreground_Callbackのテスト()
        {
            var brush = Brushes.AliceBlue;
            var editor = new TextEditor();

            editor.AsDynamic().FormattedText = null;

            editor.Foreground = brush;
            (editor.AsDynamic().FormattedText as FormattedText[]).IsNotNull();
        }
        #endregion

        #endregion

        #endregion

        #region OnApplyTemplate

        #region 正常ケース
        [Test, STAThread]
        public void OnApplyTemplate_正常ケース()
        {
            var editor = new TextEditor();
            (editor.AsDynamic().ContentHolder as ScrollViewer).IsNull();
            editor.ApplyTemplate();
            (editor.AsDynamic().ContentHolder as ScrollViewer).IsNotNull();
        }
        #endregion

        #region UpdateState

        #region 正常ケース
        [Test, STAThread]
        public void UpdateState_正常ケース()
        {
            var editor = new TextEditor();
            var win = new Window();
            win.Content = editor;
            win.Show();

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as ScrollViewer)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Unfocused");

            editor.AsDynamic().UpdateState(true);

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as ScrollViewer)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Unfocused");

            Keyboard.Focus(editor);
            editor.AsDynamic().UpdateState(true);

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as ScrollViewer)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Focused");

            win.Close();
        }
        #endregion

        #region スクロールのテスト
        [Test, STAThread]
        public void UpdateState_スクロールのテスト()
        {
            var editor = new TextEditor();
            editor.Text = string.Concat(Enumerable.Repeat("foo", 100));
            editor.Width = 100;

            var win = new Window();
            win.Content = editor;

            win.Show();

            var scroll = editor.AsDynamic().ContentHolder as ScrollViewer;
            scroll.HorizontalOffset.Is(0);

            editor.CaretIndex = 100;
            win.UpdateLayout();
            scroll.HorizontalOffset.IsNot(0);

            win.Close();
        }
        #endregion

        #endregion

        #endregion

        #region RowCount

        #region 正常ケース
        [Test, STAThread]
        public void RowCount_正常ケース()
        {
            var editor = new TextEditor();

            editor.RowCount.Is(1);

            editor.RowCount = 5;
            editor.RowCount.Is(5);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void RowCount_Callbackのテスト()
        {
            var editor = new TextEditor();

            editor.Text = "foo";
            (editor.AsDynamic().FormattedText as FormattedText[])[0].Text.Is("foo");

            editor.RowCount = 5;
            (editor.AsDynamic().FormattedText as FormattedText[])[0].Text.Is("f");
        }
        #endregion

        #endregion

        #region Text

        #region 正常ケース
        [Test, STAThread]
        public void Text_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "foo";
            editor.Text.Is("foo");
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void Text_Callbackのテスト()
        {
            var editor = new TextEditor();

            editor.RowCount = 5;
            (editor.AsDynamic().FormattedText as FormattedText[])[0].Text.Is("");

            editor.Text = "foo";
            (editor.AsDynamic().FormattedText as FormattedText[])[0].Text.Is("f");
        }
        #endregion

        #region Callback_UpdateStatesのテスト
        [Test, STAThread]
        public void Text_Callback_UpdateStatesのテスト()
        {
            var editor = new TextEditor();
            var win = new Window();
            win.Content = editor;

            win.Show();
            win.Close();

            VisualStateManager.GoToState(editor, "Focused", false);
            editor.Text = "foo";

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as FrameworkElement)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Unfocused");
        }
        #endregion

        #region Callbac_InputMaskkのテスト
        [Test, STAThread]
        public void Text_Callback_InputMaskのテスト()
        {
            var editor = new TextEditor();
            editor.InputMask.Add(new MaskingItem() { MaskPattern = "[\\r|\\n]", MaskingChar = '_' });
            editor.Text = "foo\r\nbar";
            (editor.AsDynamic().FormattedText as FormattedText[])[0].Text.Is("foo__bar");
        }
        #endregion

        #region GetTextAt

        #region 正常ケース
        [Test, STAThread]
        public void GetTextAt_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "abcabcabc";
            editor.RowCount = 3;

            (editor.AsDynamic().GetTextAt(0) as string).Is("aaa");
            (editor.AsDynamic().GetTextAt(1) as string).Is("bbb");
            (editor.AsDynamic().GetTextAt(2) as string).Is("ccc");
        }
        #endregion

        #region nullの場合
        [Test, STAThread]
        public void GetTextAt_nullの場合()
        {
            var editor = new TextEditor();
            editor.Text = null;
            (editor.AsDynamic().GetTextAt(0) as string).Is("");
        }
        #endregion

        #endregion

        #region Format

        #region 正常ケース
        [Test, STAThread]
        public void Format_正常ケース()
        {
            var editor = new TextEditor();
            var formatted = editor.AsDynamic().Format("foo") as FormattedText;
            formatted.Text.Is("foo");
        }
        #endregion

        #region nullの場合
        [Test, STAThread]
        public void Format_nullの場合()
        {
            var editor = new TextEditor();
            (editor.AsDynamic().Format(null as string) as FormattedText).Text.Is("");
        }
        #endregion

        #endregion

        #region MeasureString

        #region 正常ケース
        [Test, STAThread]
        public void MeasureString_正常ケース()
        {
            var editor = new TextEditor();
            editor.FontFamily = new FontFamily("ＭＳゴシック");

            var text = new FormattedText("foo", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("ＭＳゴシック"), 12, Brushes.Black);
            Size size = editor.AsDynamic().MeasureString("foo");
            size.Width.Is(text.WidthIncludingTrailingWhitespace);
            size.Height.Is(text.Height);

            editor.FontSize = 24;
            text = new FormattedText("foo", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("ＭＳゴシック"), 24, Brushes.Black);
            size = editor.AsDynamic().MeasureString("foo");
            size.Width.Is(text.WidthIncludingTrailingWhitespace);
            size.Height.Is(text.Height);
        }
        #endregion

        #region 空白
        [Test, STAThread]
        public void MeasureString_空白()
        {
            var editor = new TextEditor();
            editor.FontFamily = new FontFamily("ＭＳゴシック");

            var text = new FormattedText(" ", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("ＭＳゴシック"), 12, Brushes.Black);
            Size size = editor.AsDynamic().MeasureString(" ");
            size.Width.Is(text.WidthIncludingTrailingWhitespace);
            size.Height.Is(text.Height);
        }
        #endregion

        #region null
        [Test, STAThread]
        public void MeasureString_null()
        {
            var editor = new TextEditor();
            editor.FontFamily = new FontFamily("ＭＳゴシック");

            Size size = editor.AsDynamic().MeasureString(null as string);
            size.Width.Is(0);
            size.Height.Is(0);
        }
        #endregion

        #region char
        [Test, STAThread]
        public void MeasureString_char()
        {
            var editor = new TextEditor();
            editor.FontFamily = new FontFamily("ＭＳゴシック");

            var text = new FormattedText("f", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("ＭＳゴシック"), 12, Brushes.Black);
            Size size = editor.AsDynamic().MeasureString('f');
            size.Width.Is(text.WidthIncludingTrailingWhitespace);
            size.Height.Is(text.Height);
        }
        #endregion

        #endregion

        #endregion

        #region CaretIndex

        #region 正常ケース
        [Test, STAThread]
        public void CaretIndex_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "foo";
            editor.CaretIndex.Is(0);
            editor.CaretIndex = 1;
            editor.CaretIndex.Is(1);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void CaretIndex_Callbackのテスト()
        {
            var editor = new TextEditor();
            editor.Text = "foo";

            var win = new Window();
            win.Content = editor;

            win.Show();
            win.Close();

            VisualStateManager.GoToState(editor, "Focused", false);
            editor.CaretIndex = 1;

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as FrameworkElement)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Unfocused");
        }
        #endregion

        #region TextLengthを超える場合
        [Test, STAThread]
        public void CaretIndex_TextLengthを超える場合()
        {
            var editor = new TextEditor();
            editor.Text = "foo";
            editor.CaretIndex = 5;
            editor.CaretIndex.Is(2);
        }
        #endregion

        #region マイナス値の場合
        [Test, STAThread]
        public void CaretIndex_マイナス値の場合()
        {
            var editor = new TextEditor();
            editor.Text = "foo";
            editor.CaretIndex = -5;
            editor.CaretIndex.Is(0);
        }
        #endregion

        #region GetCaretIndexAt

        #region 正常ケース
        [Test, STAThread]
        public void GetCaretIndexAt_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "fffoooooo";
            editor.RowCount = 3;

            int index = editor.AsDynamic().GetCaretIndexAt(new Point(0, 0));
            index.Is(0);

            index = editor.AsDynamic().GetCaretIndexAt(new Point(10, 20));
            index.Is(4);

            index = editor.AsDynamic().GetCaretIndexAt(new Point(15, 40));
            index.Is(8);
        }
        #endregion

        #region 範囲外
        [Test, STAThread]
        public void GetCaretIndexAt_範囲外()
        {
            var editor = new TextEditor();
            editor.Text = "fffoooooo";
            editor.RowCount = 3;

            int index = editor.AsDynamic().GetCaretIndexAt(new Point(-1, -1));
            index.Is(0);

            index = editor.AsDynamic().GetCaretIndexAt(new Point(100, 100));
            index.Is(8);
        }
        #endregion

        #endregion

        #region GetCaretPosition

        #region 正常ケース
        [Test, STAThread]
        public void GetCaretPosition_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "fffoooooo";
            editor.RowCount = 3;

            Point position = editor.AsDynamic().GetCaretPosition();
            position.X.Is(0);
            position.Y.Is(0);

            var text = new FormattedText("f", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            editor.CaretIndex = 4;
            position = editor.AsDynamic().GetCaretPosition();
            position.X.Is(text.Width);
            position.Y.Is(text.Height);

            text = new FormattedText("fo", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            editor.CaretIndex = 8;
            position = editor.AsDynamic().GetCaretPosition();
            position.X.Is(text.Width);
            position.Y.Is(text.Height * 2);
        }
        #endregion

        #endregion

        #region GetCaretPositionAt

        #region 正常ケース
        [Test, STAThread]
        public void GetCaretPositionAt_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "fffoooooo";
            editor.RowCount = 3;

            Point position = editor.AsDynamic().GetCaretPositionAt(0);
            position.X.Is(0);
            position.Y.Is(0);

            var text = new FormattedText("f", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            position = editor.AsDynamic().GetCaretPositionAt(4);
            position.X.Is(text.Width);
            position.Y.Is(text.Height);

            text = new FormattedText("fo", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            position = editor.AsDynamic().GetCaretPositionAt(8);
            position.X.Is(text.Width);
            position.Y.Is(text.Height * 2);
        }
        #endregion

        #endregion

        #endregion

        #region CaretColor

        #region 正常ケース
        [Test, STAThread]
        public void CaretColor_正常ケース()
        {
            var editor = new TextEditor();
            editor.CaretColor.Is(SystemColors.HighlightColor);

            editor.CaretColor = Colors.Blue;
            editor.CaretColor.Is(Colors.Blue);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void CaretColor_Callbackのテスト()
        {
            var editor = new TextEditor();
            var win = new Window();
            win.Content = editor;

            win.Show();
            win.Close();

            VisualStateManager.GoToState(editor, "Focused", false);
            editor.CaretColor = Colors.Blue;

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as FrameworkElement)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Unfocused");
        }
        #endregion

        #endregion

        #region CaretOpacity

        #region 正常ケース
        [Test, STAThread]
        public void CaretOpacity_正常ケース()
        {
            var editor = new TextEditor();
            editor.CaretOpacity.Is(0.5);

            editor.CaretOpacity = 1;
            editor.CaretOpacity.Is(1);
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void CaretOpacity_Callbackのテスト()
        {
            var editor = new TextEditor();
            var win = new Window();
            win.Content = editor;

            win.Show();
            win.Close();

            VisualStateManager.GoToState(editor, "Focused", false);
            editor.CaretOpacity = 1;

            VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as FrameworkElement)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name.Is("Unfocused");
        }
        #endregion

        #endregion

        #region MouseEvents

        #region 正常ケース
        [Test, STAThread]
        public void MouseEvents_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "foofoofoo";
            editor.RowCount = 3;

            editor.AsDynamic().UpdateCaretIndex(new Point(10, 20));
            editor.CaretIndex.Is(4);
            editor.IsFocused.Is(true);
        }
        #endregion

        #endregion

        #region KeyEvents

        #region TextInput

        #region 正常ケース
        [Test, STAThread]
        public void TextInput_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "foofoofoo";
            editor.RowCount = 3;

            editor.CaretIndex = 0;
            editor.AsDynamic().UpdateText("b");
            editor.Text.Is("boofoofoo");
            editor.CaretIndex.Is(1);

            editor.CaretIndex = 8;
            editor.AsDynamic().UpdateText("e");
            editor.Text.Is("boofoofoe");
            editor.CaretIndex.Is(8);

            editor.CaretIndex = 3;
            editor.AsDynamic().UpdateText("hoge");
            editor.Text.Is("boohogeoe");
            editor.CaretIndex.Is(7);
        }
        #endregion

        #endregion

        #region KeyDown

        #region 正常ケース
        [Test, STAThread]
        public void KeyDown_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "foofoofoo";
            editor.RowCount = 3;
            editor.CaretIndex = 4;

            bool ret;
            ret = editor.AsDynamic().UpdateCaretIndex(Key.Right, ModifierKeys.None);
            editor.CaretIndex.Is(7);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.Left, ModifierKeys.None);
            editor.CaretIndex.Is(4);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.Up, ModifierKeys.None);
            editor.CaretIndex.Is(3);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.Down, ModifierKeys.None);
            editor.CaretIndex.Is(4);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.Home, ModifierKeys.None);
            editor.CaretIndex.Is(1);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.End, ModifierKeys.None);
            editor.CaretIndex.Is(7);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.Home, ModifierKeys.Control);
            editor.CaretIndex.Is(0);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.End, ModifierKeys.Control);
            editor.CaretIndex.Is(8);
            ret.Is(true);
        }
        #endregion

        #region 折り返しのテスト
        [Test, STAThread]
        public void KeyDown_折り返しのテスト()
        {
            var editor = new TextEditor();
            editor.Text = "foofoofoo";
            editor.RowCount = 3;

            editor.CaretIndex = 4;
            editor.AsDynamic().UpdateCaretIndex(Key.Left, ModifierKeys.None);
            editor.CaretIndex.Is(1);
            editor.AsDynamic().UpdateCaretIndex(Key.Left, ModifierKeys.None);
            editor.CaretIndex.Is(1);

            editor.CaretIndex = 4;
            editor.AsDynamic().UpdateCaretIndex(Key.Right, ModifierKeys.None);
            editor.CaretIndex.Is(7);
            editor.AsDynamic().UpdateCaretIndex(Key.Right, ModifierKeys.None);
            editor.CaretIndex.Is(7);

            editor.CaretIndex = 3;
            editor.AsDynamic().UpdateCaretIndex(Key.Up, ModifierKeys.None);
            editor.CaretIndex.Is(2);

            editor.CaretIndex = 5;
            editor.AsDynamic().UpdateCaretIndex(Key.Down, ModifierKeys.None);
            editor.CaretIndex.Is(6);

            editor.CaretIndex = 3;
            editor.AsDynamic().UpdateCaretIndex(Key.Left, ModifierKeys.None);
            editor.CaretIndex.Is(0);
            editor.AsDynamic().UpdateCaretIndex(Key.Left, ModifierKeys.None);
            editor.CaretIndex.Is(0);

            editor.CaretIndex = 5;
            editor.AsDynamic().UpdateCaretIndex(Key.Right, ModifierKeys.None);
            editor.CaretIndex.Is(8);
            editor.AsDynamic().UpdateCaretIndex(Key.Right, ModifierKeys.None);
            editor.CaretIndex.Is(8);
        }
        #endregion

        #region 特殊キー
        [Test, STAThread]
        public void KeyDown_特殊キー()
        {
            var editor = new TextEditor();
            editor.Text = "foofoofoo";
            editor.RowCount = 3;
            editor.CaretIndex = 4;

            bool ret;
            ret = editor.AsDynamic().UpdateCaretIndex(Key.Escape, ModifierKeys.None);
            editor.CaretIndex.Is(4);
            ret.Is(true);

            ret = editor.AsDynamic().UpdateCaretIndex(Key.Back, ModifierKeys.None);
            editor.CaretIndex.Is(3);
            ret.Is(true);
        }
        #endregion

        #endregion

        #endregion

        #region FocusEvents

        #region 正常ケース
        [Test, STAThread]
        public void FocusEvents_正常ケース()
        {
            var editor = new TextEditor();
            var text = new TextBox();

            var panel = new StackPanel();
            panel.Children.Add(editor);
            panel.Children.Add(text);

            var win = new Window();
            win.Content = panel;

            win.Show();

            Func<string> GetState = () => VisualStateManager
                .GetVisualStateGroups(editor.AsDynamic().ContentHolder as FrameworkElement)
                .OfType<VisualStateGroup>()
                .FirstOrDefault(g => g.Name == "FocusStates")
                .CurrentState.Name;

            editor.Focus();
            GetState().Is("Focused");

            text.Focus();
            GetState().Is("Unfocused");

            win.Close();
        }
        #endregion

        #endregion

        #region Highlight

        #region HighlightedText

        #region 正常ケース
        [Test, STAThread]
        public void HighlightedText_正常ケース()
        {
            var editor = new TextEditor();
            editor.HighlightedText.IsNull();

            editor.HighlightedText = "foo";
            editor.HighlightedText.Is("foo");
        }
        #endregion

        #region Callbackのテスト
        [Test, STAThread]
        public void HighlightedText_Callbackのテスト()
        {
            var editor = new TextEditor();
            editor.Text = "abcdefabc";
            editor.HighlightedText = "abc";
            (editor.AsDynamic().markerBounds as Rect[]).Length.Is(2);
        }
        #endregion

        #endregion

        #region HighlightColor

        #region 正常ケース
        [Test, STAThread]
        public void HighlightColor_正常ケース()
        {
            var editor = new TextEditor();
            editor.HighlightColor.Is(Colors.Yellow);

            editor.HighlightColor = Colors.AliceBlue;
            editor.HighlightColor.Is(Colors.AliceBlue);
        }
        #endregion

        #endregion

        #region HighlightOpacity

        #region 正常ケース
        [Test, STAThread]
        public void HighlightOpacity_正常ケース()
        {
            var editor = new TextEditor();
            editor.HighlightOpacity.Is(0.5);

            editor.HighlightOpacity = 0.3;
            editor.HighlightOpacity.Is(0.3);
        }
        #endregion

        #region 値の強制のテスト
        [Test, STAThread]
        public void HighlightOpacity_値の強制のテスト()
        {
            var editor = new TextEditor();
            
            editor.HighlightOpacity = 0;
            editor.HighlightOpacity.Is(0);

            editor.HighlightOpacity = -0.1;
            editor.HighlightOpacity.Is(0);

            editor.HighlightOpacity = 1;
            editor.HighlightOpacity.Is(1);

            editor.HighlightOpacity = 1.1;
            editor.HighlightOpacity.Is(1);
        }
        #endregion

        #endregion

        #region Highlight

        #region 正常ケース
        [Test, STAThread]
        public void Highlight_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "abcdefabc";
            editor.Highlight("abc");
            (editor.AsDynamic().markerBounds as Rect[]).Length.Is(2);
        }
        #endregion

        #region hitしない場合
        [Test, STAThread]
        public void Highlight_hitしない場合()
        {
            var editor = new TextEditor();
            editor.Text = "abcdefabc";
            editor.Highlight("foo");
            (editor.AsDynamic().markerBounds as Rect[]).Length.Is(0);
        }
        #endregion

        #endregion

        #region ClearHighlights

        #region 正常ケース
        [Test, STAThread]
        public void ClearHighlights_正常ケース()
        {
            var editor = new TextEditor();

            editor.ClearHighlights();
            (editor.AsDynamic().markerBounds as Rect[]).Length.Is(0);

            editor.Text = "abcdefabc";
            editor.Highlight("abc");
            (editor.AsDynamic().markerBounds as Rect[]).Length.Is(2);

            editor.ClearHighlights();
            (editor.AsDynamic().markerBounds as Rect[]).Length.Is(0);
        }
        #endregion

        #endregion

        #region GetTextBounds

        #region 正常ケース
        [Test, STAThread]
        public void GetTextBounds_正常ケース()
        {
            var editor = new TextEditor();
            editor.Text = "abcde";
            editor.RowCount = 1;

            var bounds = editor.AsDynamic().GetTextBounds("cd") as Rect[];
            bounds.Length.Is(1);

            var text = new FormattedText("ab", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            var text2 = new FormattedText("cd", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            bounds[0].Top.Is(0);
            bounds[0].Left.Is(text.Width);
            bounds[0].Right.Is(text.Width + text2.Width);
            bounds[0].Bottom.Is(text.Height);
        }
        #endregion

        #region 複数hitする場合
        [Test, STAThread]
        public void GetTextBounds_複数hitする場合()
        {
            var editor = new TextEditor();
            editor.Text = "aaaaa";
            editor.RowCount = 1;

            var bounds = editor.AsDynamic().GetTextBounds("a") as Rect[];
            bounds.Length.Is(5);

            var text = new FormattedText("a", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            bounds[0].Top.Is(0);
            bounds[0].Left.Is(0);
            bounds[0].Right.Is(text.Width);
            bounds[0].Bottom.Is(text.Height);

            bounds[1].Top.Is(0);
            bounds[1].Left.Is(text.Width);
            bounds[1].Right.Is(text.Width * 2);
            bounds[1].Bottom.Is(text.Height);

            bounds[2].Top.Is(0);
            bounds[2].Left.Is(text.Width * 2);
            bounds[2].Right.Is(text.Width * 3);
            bounds[2].Bottom.Is(text.Height);

            bounds[3].Top.Is(0);
            bounds[3].Left.Is(text.Width * 3);
            bounds[3].Right.Is(text.Width * 4);
            bounds[3].Bottom.Is(text.Height);

            bounds[4].Top.Is(0);
            bounds[4].Left.Is(text.Width * 4);
            bounds[4].Right.Is(text.Width * 5);
            bounds[4].Bottom.Is(text.Height);
        }
        #endregion

        #region hitしない場合
        [Test, STAThread]
        public void GetTextBounds_hitしない場合()
        {
            var editor = new TextEditor();
            editor.Text = "aaaaa";
            editor.RowCount = 1;

            var bounds = editor.AsDynamic().GetTextBounds("b") as Rect[];
            bounds.Length.Is(0);
        }
        #endregion

        #region 複数行表示の場合
        [Test, STAThread]
        public void GetTextBounds_複数行表示の場合()
        {
            var editor = new TextEditor();
            editor.Text = "abcdefabc";
            editor.RowCount = 2;

            var bounds = editor.AsDynamic().GetTextBounds("abc") as Rect[];
            bounds.Length.Is(6);

            var a = new FormattedText("a", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            var b = new FormattedText("b", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            var c = new FormattedText("c", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            var d = new FormattedText("d", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            var e = new FormattedText("e", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            var f = new FormattedText("f", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);

            bounds[0].Top.Is(0);
            bounds[0].Left.Is(0);
            bounds[0].Right.Is(a.Width);
            bounds[0].Bottom.Is(a.Height);

            bounds[1].Top.Is(a.Height);
            bounds[1].Left.Is(0);
            bounds[1].Right.Is(b.Width);
            bounds[1].Bottom.Is(a.Height + b.Height);

            bounds[2].Top.Is(0);
            bounds[2].Left.Is(a.Width);
            bounds[2].Right.Is(a.Width + c.Width);
            bounds[2].Bottom.Is(c.Height);

            bounds[3].Top.Is(0);
            bounds[3].Left.Is(a.Width + c.Width + e.Width);
            bounds[3].Right.Is(a.Width + c.Width + e.Width + a.Width);
            bounds[3].Bottom.Is(a.Height);

            bounds[4].Top.Is(a.Height);
            bounds[4].Left.Is(b.Width + d.Width + f.Width);
            bounds[4].Right.Is(b.Width + d.Width + f.Width + b.Width);
            bounds[4].Bottom.Is(a.Height + b.Height);

            bounds[5].Top.Is(0);
            bounds[5].Left.Is(a.Width + c.Width + e.Width + a.Width);
            bounds[5].Right.Is(a.Width + c.Width + e.Width + a.Width + c.Width);
            bounds[5].Bottom.Is(c.Height);
        }
        #endregion

        #region 正規表現
        [Test, STAThread]
        public void GetTextBounds_正規表現()
        {
            var editor = new TextEditor();
            editor.Text = "abcde";
            editor.RowCount = 1;

            var bounds = editor.AsDynamic().GetTextBounds("^ab") as Rect[];
            bounds.Length.Is(1);

            var text = new FormattedText("ab", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(editor.FontFamily, editor.FontStyle, editor.FontWeight, editor.FontStretch), editor.FontSize, Brushes.Black);
            bounds[0].Top.Is(0);
            bounds[0].Left.Is(0);
            bounds[0].Right.Is(text.Width);
            bounds[0].Bottom.Is(text.Height);
        }
        #endregion

        #region null
        [Test, STAThread]
        public void GetTextBounds_null()
        {
            var editor = new TextEditor();
            editor.Text = "abcde";
            editor.RowCount = 1;

            var bounds = editor.AsDynamic().GetTextBounds(null as string) as Rect[];
            bounds.Length.Is(0);
        }
        #endregion

        #region 空文字
        [Test, STAThread]
        public void GetTextBounds_空文字()
        {
            var editor = new TextEditor();
            editor.Text = "abcde";
            editor.RowCount = 1;

            var bounds = editor.AsDynamic().GetTextBounds("") as Rect[];
            bounds.Length.Is(0);
        }
        #endregion

        #endregion

        #endregion

        #region InputMask

        #region 正常ケース
        [Test, STAThread]
        public void InputMask_正常ケース()
        {
            var editor = new TextEditor();
            editor.InputMask.Count.Is(0);

            var mask = new MaskingItem() { MaskPattern = "foo", MaskingChar = '*' };
            editor.InputMask.Add(mask);
            editor.InputMask.Count().Is(1);
            editor.InputMask[0].MaskPattern.Is("foo");
            editor.InputMask[0].MaskingChar.Is('*');
        }
        #endregion

        #region Mask

        #region 正常ケース
        [Test, STAThread]
        public void Mask_正常ケース()
        {
            var editor = new TextEditor();
            editor.InputMask.Add(new MaskingItem() { MaskPattern = "a", MaskingChar = 'A' });
            editor.InputMask.Add(new MaskingItem() { MaskPattern = "[\\r|\\n]", MaskingChar = '.' });

            (editor.AsDynamic().Mask("abc\r\ndef") as string).Is("Abc..def");
        }
        #endregion

        #endregion

        #endregion

        #region TextView

        #region _ctor

        #region 正常ケース
        [Test, STAThread]
        public void TextView_ctor_正常ケース()
        {
            var editor = new TextEditor();
            var view = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+TextView, FileAid").CreateInstance(editor);
            (view.AsDynamic().Owner as TextEditor).Is(editor);
        }
        #endregion

        #endregion

        #region MeasureOverride

        #region 正常ケース
        [Test, STAThread]
        public void TextView_MeasureOverride_正常ケース()
        {
            var editor = new TextEditor();

            var win = new Window();
            win.Content = editor;

            var text = new FormattedText("foo", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("ＭＳゴシック"), 12, Brushes.Black);
            editor.AsDynamic().FormattedText = new[] { text };

            win.Show();
            win.Close();

            var size = (Size)editor.AsDynamic().view.DesiredSize;
            size.Width.Is(text.WidthIncludingTrailingWhitespace);
            size.Height.Is(text.Height);
        }
        #endregion

        #region FormattedTextがnull
        [Test, STAThread]
        public void TextView_MeasureOverride_FormattedTextがnull()
        {
            var editor = new TextEditor();

            var win = new Window();
            win.Content = editor;

            win.Show();
            win.Close();

            var size = (Size)editor.AsDynamic().view.DesiredSize;
            size.Width.Is(0);
            size.Height.Is(0);
        }
        #endregion

        #endregion

        #endregion

        #region Caret

        #region _ctor

        #region 正常ケース
        [Test, STAThread]
        public void Caret_ctor_正常ケース()
        {
            var editor = new TextEditor();
            var view = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+TextView, FileAid").CreateInstance(editor);
            var caret = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+Caret, FileAid").CreateInstance(view, editor);
            (caret.AsDynamic().AdornedElement as UIElement).Is(view);
            (caret.AsDynamic().Owner as TextEditor).Is(editor);
        }
        #endregion

        #endregion

        #endregion

        #region Marker

        #region _ctor

        #region 正常ケース
        [Test, STAThread]
        public void Marker_ctor_正常ケース()
        {
            var editor = new TextEditor();
            var view = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+TextView, FileAid").CreateInstance(editor);
            var marker = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+Marker, FileAid").CreateInstance(view, editor);
            (marker.AsDynamic().AdornedElement as UIElement).Is(view);
            (marker.AsDynamic().Owner as TextEditor).Is(editor);
        }
        #endregion

        #endregion

        #endregion

        #region ImeSupport

        #region Editor

        #region 正常ケース
        [Test, STAThread]
        public void ImeSupport_Editor_正常ケース()
        {
            var editor = new TextEditor();
            var ime = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeSupport, FileAid").CreateInstance(editor);
            ime.AsDynamic().Editor = editor;
            (ime.AsDynamic().Editor as TextEditor).Is(editor);
        }
        #endregion

        #endregion

        #region CurrentContext

        #region 正常ケース
        [Test, STAThread]
        public void ImeSupport_CurrentContext_正常ケース()
        {
            var editor = new TextEditor();
            var p = new IntPtr(1);
            var ime = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeSupport, FileAid").CreateInstance(editor);
            ime.AsDynamic().CurrentContext = p;
            ((IntPtr)ime.AsDynamic().CurrentContext).Is(p);
        }
        #endregion

        #endregion

        #region VisualSource

        #region 正常ケース
        [Test, STAThread]
        public void ImeSupport_VisualSource_正常ケース()
        {
            var editor = new TextEditor();
            var win = new Window();
            var source = PresentationSource.FromVisual(win) as HwndSource;
            var ime = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeSupport, FileAid").CreateInstance(editor);
            ime.AsDynamic().VisualSource = source;
            (ime.AsDynamic().VisualSource as HwndSource).Is(source);
        }
        #endregion

        #endregion

        #region CreateContext / ClearContext

        #region 正常ケース
        [Test, STAThread]
        public void ImeSupport_CreateContext_ClearContext_正常ケース()
        {
            var editor = new TextEditor();
            var win = new Window();
            win.Content = editor;
            win.Show();

            var source = PresentationSource.FromVisual(win) as HwndSource;
            var ime = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeSupport, FileAid").CreateInstance(editor);

            ime.AsDynamic().CreateContext();

            ((IntPtr)ime.AsDynamic().CurrentContext).IsNot(IntPtr.Zero);
            (ime.AsDynamic().VisualSource as HwndSource).IsNotNull();

            ime.AsDynamic().ClearContext();

            ((IntPtr)ime.AsDynamic().CurrentContext).Is(IntPtr.Zero);
            (ime.AsDynamic().VisualSource as HwndSource).IsNull();

            win.Close();
        }
        #endregion

        #endregion

        #region Dispose

        #region 正常ケース
        [Test, STAThread]
        public void ImeSupport_Dispose_正常ケース()
        {
            var editor = new TextEditor();
            var win = new Window();
            win.Content = editor;
            win.Show();

            var source = PresentationSource.FromVisual(win) as HwndSource;
            var ime = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeSupport, FileAid").CreateInstance(editor);

            ime.AsDynamic().CreateContext();

            ((IntPtr)ime.AsDynamic().CurrentContext).IsNot(IntPtr.Zero);
            (ime.AsDynamic().VisualSource as HwndSource).IsNotNull();

            ime.AsDynamic().Dispose();
            ime.AsDynamic().Dispose();

            ((IntPtr)ime.AsDynamic().CurrentContext).Is(IntPtr.Zero);
            (ime.AsDynamic().VisualSource as HwndSource).IsNull();

            win.Close();
        }
        #endregion

        #endregion

        #endregion

        #region ImeNativeWrapper

        #region GetContext / ReleaseContext

        #region 正常ケース
        [Test, STAThread]
        public void ImeNativeWrapper_GetContext_ReleaseContext_正常ケース()
        {
            var win = new Window();
            win.Show();

            var source = PresentationSource.FromVisual(win) as HwndSource;
            var context = IntPtr.Zero;
            var type = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeNativeWrapper, FileAid");

            try
            {
                context = type.AsDynamic().GetContext(source);
                context.IsNot(IntPtr.Zero);
            }
            finally
            {
                ((bool)type.AsDynamic().ReleaseContext(source, context)).Is(true);
                win.Close();
            }
        }
        #endregion

        #region sourceがnull
        [Test, STAThread]
        public void ImeNativeWrapper_GetContext_ReleaseContext_sourceがnull()
        {
            var type = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeNativeWrapper, FileAid");
            ((IntPtr)type.AsDynamic().GetContext(null as HwndSource)).Is(IntPtr.Zero);
            ((bool)type.AsDynamic().ReleaseContext(null as HwndSource, IntPtr.Zero)).Is(false);
        }
        #endregion

        #endregion

        #region SetCompositionWindow

        #region 正常ケース
        [Test, STAThread]
        public void ImeNativeWrapper_SetCompositionWindow_正常ケース()
        {
            var win = new Window();
            win.Show();

            var source = PresentationSource.FromVisual(win) as HwndSource;
            var context = IntPtr.Zero;
            var type = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeNativeWrapper, FileAid");

            try
            {
                context = type.AsDynamic().GetContext(source);

                ((bool)type.AsDynamic().SetCompositionWindow(context, Rect.Empty, Rect.Empty)).Is(true);
            }
            finally
            {
                ((bool)type.AsDynamic().ReleaseContext(source, context)).Is(true);
                win.Close();
            }
        }
        #endregion

        #endregion

        #region SetCompositionFont

        #region 正常ケース
        [Test, STAThread]
        public void ImeNativeWrapper_SetCompositionFont_正常ケース()
        {
            var win = new Window();
            win.Show();

            var source = PresentationSource.FromVisual(win) as HwndSource;
            var context = IntPtr.Zero;
            var type = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeNativeWrapper, FileAid");

            try
            {
                context = type.AsDynamic().GetContext(source);

                ((bool)type.AsDynamic().SetCompositionFont(context, win.FontFamily, Rect.Empty)).Is(true);
            }
            finally
            {
                ((bool)type.AsDynamic().ReleaseContext(source, context)).Is(true);
                win.Close();
            }
        }
        #endregion

        #endregion

        #region NotifyIme

        #region 正常ケース
        [Test, STAThread]
        public void ImeNativeWrapper_NotifyIme_正常ケース()
        {
            var win = new Window();
            win.Show();

            var source = PresentationSource.FromVisual(win) as HwndSource;
            var context = IntPtr.Zero;
            var type = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeNativeWrapper, FileAid");

            try
            {
                context = type.AsDynamic().GetContext(source);

                ((bool)type.AsDynamic().NotifyIme(context)).Is(true);
            }
            finally
            {
                ((bool)type.AsDynamic().ReleaseContext(source, context)).Is(true);
                win.Close();
            }
        }
        #endregion

        #endregion

        #region GetTextFrameworkThreadManager

        #region 正常ケース
        [Test]
        public void ImeNativeWrapper_GetTextFrameworkThreadManager_正常ケース()
        {
            var type = Type.GetType("Uhi.Applications.FileAid.View.TextEditor+ImeNativeWrapper, FileAid");
            (type.AsDynamic().GetTextFrameworkThreadManager() as object).IsNotNull();
        }
        #endregion

        #endregion

        #endregion
    }
}
